﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using Av.Utils;

namespace iAts
{

    /// <summary>
    /// Class to listen for events (CSTA protocol)
    /// </summary>
    public class AtsListener
    {

        protected Socket _socketToListen = null;

        TcpListener _tcpListener = null;

        protected int _portToListen = 0;

        public bool IsListening
        {
            get
            {
                return _socketToListen == null ? false : _socketToListen.Connected;
            }
        }

        public AtsListener(int port)
        {
            if(port < IPEndPoint.MinPort || port > IPEndPoint.MaxPort )
            {
                throw new ArgumentOutOfRangeException(string.Format("Listening port should be in range of [{0} - {1}]!", IPEndPoint.MinPort, IPEndPoint.MaxPort));
            }
            _portToListen = port;
        }

        public void Start()
        {
            Log4cs.Log("Starting listening socket on {0} port...", _portToListen);
            if(_socketToListen != null)
            {
                Log4cs.Log(Importance.Error, "Listening socket is not null, probably is in use!");
                throw new Exception("Socket is not null!");
            }

            
            _tcpListener = new TcpListener(IPAddress.Any, _portToListen);
            _tcpListener.Start(5);
            _tcpListener.BeginAcceptTcpClient(OnTcpClientAccepted, null);
            

            //Log4cs.Log("Going to bind to {0}:{1}", IPAddress.Loopback.Address, _portToListen);
            //_socketToListen = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //_socketToListen.Bind(new IPEndPoint(IPAddress.Loopback.Address, _portToListen));
            //_socketToListen.Listen(5);
            //_socketToListen.BeginAccept(OnIncomingConnectionAccepted, null);
        }

        private void OnTcpClientAccepted(IAsyncResult r)
        {
            try
            {
                TcpClient client = _tcpListener.EndAcceptTcpClient(r);
                _tcpListener.BeginAcceptSocket(OnTcpClientAccepted, null);

                AtsClient atsClient = new AtsClient(client);
                NetworkStream ns = client.GetStream();
                ns.BeginRead(atsClient.ReceivedBytes, 0, atsClient.ReceivedBytes.Length, OnTcpClientRead, atsClient);
            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error accepting client!");
                Log4cs.Log(Importance.Debug, ex.ToString());
            }
        }

        private void OnTcpClientRead(IAsyncResult r)
        {
            AtsClient client = r.AsyncState as AtsClient;
            Log4cs.Log("Reading data from connected ATS...");
            if(client != null)
            {
                try
                {
                    NetworkStream ns = client.TcpClient.GetStream();
                    int totalBytes = ns.EndRead(r);
                    if(totalBytes > 0)
                    {
                        Log4cs.Log("Got {0} bytes from connected ATS", totalBytes);
                        client.TotalBytesReceived += totalBytes;
                        Log4cs.Log("{0}", Encoding.UTF8.GetString(client.ReceivedBytes, 0, client.TotalBytesReceived));
                    } else
                    {
                        Log4cs.Log("There is no data available from ATS, disconnecting it");
                        ns.Close();
                        client.TcpClient.Close();
                    }
                } catch(Exception ex)
                {
                    Log4cs.Log(Importance.Error, "Error reading data from connected ATS!");
                    Log4cs.Log(Importance.Debug, ex.ToString());
                }
            }
        }

        public void Stop()
        {
            if(_socketToListen != null)
            {
                Log4cs.Log("Closing listening socket...");
                _socketToListen.Shutdown(SocketShutdown.Both);
                _socketToListen.Close(1);
                _socketToListen = null;
            }
        }

        protected void OnIncomingConnectionAccepted(object state)
        {
            IAsyncResult res = state as IAsyncResult;
            Log4cs.Log("New connection is accepted...");
        }

    }
}
